# -*- coding: utf-8 -*-
import random

from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django_redis import get_redis_connection

from utils import encrypt
from utils.tencent.sms import send_sms_single
from web.models import UserInfo
from web import models
from web.forms.bootstrap import BootStrapForm


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    phone = forms.CharField(label='手机', validators=[
        RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '数据号格式错误'),
    ])
    password = forms.CharField(label='密码', min_length=8, max_length=64, error_messages={
        'min_length': '密码最少8个字符',
        'max_length': '密码最多64个字符', }, widget=forms.PasswordInput())
    confirm_password = forms.CharField(label='确认密码', min_length=8, max_length=64, error_messages={
        'min_length': '密码最少8个字符',
        'max_length': '密码最多64个字符', }, widget=forms.PasswordInput())
    code = forms.CharField(label='验证码')
    
    class Meta:
        model = UserInfo
        # 默认字段在页面显示的顺序
        # fields = '__all__'
        # 自定义显示顺序
        fields = ['name', 'email', 'password', 'confirm_password', 'phone', 'code']
    
    def clean_name(self):
        name = self.cleaned_data['name']
        exists = models.UserInfo.objects.filter(name=name).exists()
        if exists:
            raise ValidationError('用户名已存在')
        return name
    
    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email
    
    def clean_password(self):
        password = self.cleaned_data['password']
        # 加密密码
        return encrypt.md5(password)
    
    def clean_confirm_password(self):
        password = self.cleaned_data['password']
        confirm_password = encrypt.md5(self.cleaned_data['confirm_password'])
        if password != confirm_password:
            raise ValidationError('两次密码不一致')
        return confirm_password
    
    def clean_phone(self):
        phone = self.cleaned_data['phone']
        exists = models.UserInfo.objects.filter(phone=phone).exists()
        if exists:
            raise ValidationError('手机号已注册')
        return phone
    
    def clean_code(self):
        code = self.cleaned_data['code']
        phone = self.cleaned_data['phone']
        conn = get_redis_connection()
        redis_code = conn.get(phone)
        
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')
        
        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')
        return code


class SendSmsForm(BootStrapForm, forms.Form):
    """只做页面的数据校验，所以只继承forms.Form"""
    phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '数据号格式错误'), ])
    
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.request = request
    
    # 构子函数
    def clean_phone(self):
        phone = self.cleaned_data['phone']
        
        # 短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        tpl_id = settings.TENCENT_SMS_TEMPLATE_ID[tpl]
        if not tpl_id:
            raise ValidationError('短信模板错误')
        
        # 是否存在
        exist = models.UserInfo.objects.filter(phone=phone).exists()
        if tpl == 'login':
            if not exist:
                raise ValidationError('手机号还没有注册存在')
        else:
            if exist:
                raise ValidationError('手机号已经存在')
        
        # 生成注册码 保存到缓存
        code = random.randrange(1000, 9999)
        sms = send_sms_single(phone, tpl_id, [code, ])
        if sms['result'] != 0:
            raise ValidationError('短信发送失败，{}'.format(sms['errmsg']))
        
        conn = get_redis_connection()
        # 有效期 一分钟
        conn.set(phone, code, ex=60)
        
        return phone


class LoginSMSForm(BootStrapForm, forms.Form):
    phone = forms.CharField(label='手机', validators=[
        RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '数据号格式错误'),
    ])
    code = forms.CharField(label='验证码')
    
    class Meta:
        model = UserInfo
        # 默认字段在页面显示的顺序
        # fields = '__all__'
        # 自定义显示顺序
        fields = ['phone', 'code']
    
    def clean_phone(self):
        phone = self.cleaned_data['phone']
        exists = models.UserInfo.objects.filter(phone=phone).exists()
        # user_obj = models.UserInfo.objects.filter(phone=phone).first()
        if exists:
            raise ValidationError('手机号不存在')
        
        return exists
    
    def clean_code(self):
        code = self.cleaned_data['code']
        phone = self.cleaned_data.get('phone')
        if not phone:
            return code
        
        conn = get_redis_connection()
        redis_code = conn.get(phone)
        
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')
        
        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')
        return code


class LoginForm(BootStrapForm, forms.Form):
    # name = forms.CharField(label='用户名')
    name = forms.CharField(label='邮箱或手机号')
    # render_value=True：保存密码记录在输入框中
    password = forms.CharField(label='密码', widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='验证码')
    
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request
    
    def clean_code(self):
        
        """使用钩子函数：校验验证码是否正确"""
        code = self.cleaned_data['code']
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')
        
        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码错误')
        
        return code

    def clean_password(self):
        password = self.cleaned_data['password']
        return encrypt.md5(password)
